#include "FKDrawModel.h"

#include "FKDisplayReferent.h"

#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
//#include <QOpenGLVertexArrayObject>

#include "../FKTools/FKLogger.h"

FKDrawModel::FKDrawModel(QObject* parent):QObject(parent),_verticeCount(0),
        _indexCount(0),_loaded(false),_initialized(false),_modelName(),_curObj(_objects.begin()),_mutex(){
    FK_CBEGIN
//    _vao=new QOpenGLVertexArrayObject(this);
    FK_CEND
}

FKDrawModel::~FKDrawModel(){
    FK_DBEGIN
    foreach(FKDisplayReferent* dr,_objects)dr->modelDeleted();
    delete _vertexBuffer;
    delete _indexBuffer;
    delete _normalBuffer;
    delete _colorBuffer;
    delete _texBuffer;
    FK_DEND
}

void FKDrawModel::removeReferent(FKDisplayReferent *obj){
    QMutexLocker(&(this->_mutex));
    if(obj==(*_curObj)){
        ++_curObj;
    }
    _objects.remove(obj);
//    if(_curObj==_objects.end())_curObj=_objects.begin();
//    FK_MLOGV("object removed, elapsed",_objects.size())
}

bool FKDrawModel::loadModel(QString modelName, QString path){
    if(_loaded){
        FK_MLOG("Loading model error: already loaded")
        return false;
    }else{
        _loaded=modelLoading(modelName,path);
        if(_loaded)_modelName=modelName;
        return _loaded;
    }
}

bool FKDrawModel::modelLoading(QString modelName, QString path){
    Q_UNUSED(path)
    Q_UNUSED(modelName)

    return true;
}

bool FKDrawModel::initModel(/*QString modelName, QString path*/){
    if(!_loaded){
        FK_MLOG("Initialize model error: not loaded")
        return false;
    }
    if(_initialized){
        FK_MLOG("Initialize model error: already initialized")
        return false;
    }
    _program = new QOpenGLShaderProgram(this);
    _pickingProgram = new QOpenGLShaderProgram(this);
    _initialized=modelInitialization();
    return _initialized;
}

bool FKDrawModel::modelInitialization(){
    const char *vertexShaderSource =
        "attribute highp vec4 posAttr;\n"
        "attribute lowp vec4 colAttr;\n"
        "varying lowp vec4 col;\n"
        "uniform highp mat4 matrix;\n"
        "void main() {\n"
        "   col = colAttr;\n"
        "   gl_Position = matrix * posAttr;\n"
        "}\n";
    const char *fragmentShaderSource =
        "varying lowp vec4 col;\n"
        "uniform lowp vec4 mColor;\n"
        "void main() {\n"
        "   gl_FragColor = col*mColor;\n"
        "}\n";
    const char* pickingVertexShaderSource=
            "attribute highp vec4 posAtr;\n"
            "uniform highp mat4 matrix;\n"
            "void main() {\n"
            "   gl_Position = matrix * posAtr;\n"
            "}\n";
    const char *pickingFragmentShaderSource =
        "uniform highp vec3 referentID;\n"
        "void main() {\n"
        "   gl_FragColor = vec4(referentID,1);\n"
        "}\n";
    _program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
    _program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
    _program->link();

    _pickingProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, pickingVertexShaderSource);
    _pickingProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, pickingFragmentShaderSource);
    _pickingProgram->link();

    _vertexBuffer=new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
    _indexBuffer=new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer);
    _normalBuffer=new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
    _colorBuffer=new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
    _texBuffer=new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);

    _indexBuffer->create();
    _indexBuffer->setUsagePattern( QOpenGLBuffer::StaticDraw );
    _indexBuffer->bind();
    _indexBuffer->allocate(_indexes.constData(), _indexCount * sizeof( unsigned int ) );
    _indexBuffer->release();

    _vertexBuffer->create();
    _vertexBuffer->setUsagePattern( QOpenGLBuffer::StaticDraw );
    _vertexBuffer->bind();
    _vertexBuffer->allocate(_vertices.constData(), _verticeCount * 3 * sizeof(float) );
    _vertexBuffer->release();

    _texBuffer->create();
    _texBuffer->setUsagePattern( QOpenGLBuffer::StaticDraw );
    _texBuffer->bind();
    _texBuffer->allocate(_texCoords.constData(), _verticeCount * 2 * sizeof(float) );
    _texBuffer->release();

    _normalBuffer->create();
    _normalBuffer->setUsagePattern( QOpenGLBuffer::StaticDraw );
    _normalBuffer->bind();
    _normalBuffer->allocate(_normals.constData(), _verticeCount * 3 * sizeof( float ) );
    _normalBuffer->release();

    _colorBuffer->create();
    _colorBuffer->setUsagePattern( QOpenGLBuffer::StaticDraw );
    _colorBuffer->bind();
    _colorBuffer->allocate(_colors.constData(), _verticeCount * 3 * sizeof( float ) );
    _colorBuffer->release();

//    _vao->create();
//    _vao->bind();
//    _vertexBuffer->bind();
//    _program->setAttributeBuffer("posAttr",GL_FLOAT,0,3);
//    _vertexBuffer->release();
//    _colorBuffer->bind();
//    _program->setAttributeBuffer("colAttr",GL_FLOAT,0,3);
//    _colorBuffer->release();
//    _indexBuffer->bind();
//    _vao->release();
    return true;
}

bool FKDrawModel::drawNextObject(const QMatrix4x4& pMat, const QMatrix4x4& vMat){
    QMutexLocker(&(this->_mutex));
    while(_curObj!=_objects.end()){
        FKDisplayReferent* obj= *_curObj;
        ++_curObj;
        if(obj->getVisible()){
//            FK_MLOGV("object is visible",obj->getPos())
            drawObject(obj,pMat,vMat);
            return true;
        }
    }
    _curObj=_objects.begin();
    return false;
}

void FKDrawModel::drawObject(FKDisplayReferent* obj, const QMatrix4x4& pMat, const QMatrix4x4& vMat){
    obj->customAnimation();
    QMatrix4x4 mMat(obj->getMatrix());
    _program->setUniformValue("matrix", pMat*vMat*mMat);
//    FK_MLOGV("uniform matrix",pMat*vMat*mMat)
    QColor c(obj->getColor());
//    QColor c(Qt::black);
//    FK_MLOGV("uniform color",c)
    _program->setUniformValue("mColor", QVector4D(c.redF(),c.greenF(),c.blueF(),c.alphaF()));
    _vertexBuffer->bind();
    _program->setAttributeBuffer("posAttr",GL_FLOAT,0,3);
    _program->enableAttributeArray("posAttr");
    _colorBuffer->bind();
    _program->setAttributeBuffer("colAttr",GL_FLOAT,0,3);
    _program->enableAttributeArray("colAttr");
    //_vao->bind();
    _indexBuffer->bind();
    glDrawElements(GL_TRIANGLES,_indexes.size(),GL_UNSIGNED_INT,0);
    _indexBuffer->release();
    //_vao->release();
    _program->disableAttributeArray("posAttr");
    _program->disableAttributeArray("colAttr");
    _vertexBuffer->release();
    _colorBuffer->release();
}

bool FKDrawModel::pickNextObject(const QMatrix4x4& pMat, const QMatrix4x4& vMat){
    QMutexLocker(&(this->_mutex));
    while(_curObj!=_objects.end()){
        FKDisplayReferent* obj= *_curObj;
        ++_curObj;
        if(obj->getVisible()){
            pickObject(obj,pMat,vMat);
            return true;
        }
    }
    _curObj=_objects.begin();
    return false;
}

void FKDrawModel::pickObject(FKDisplayReferent* obj, const QMatrix4x4& pMat, const QMatrix4x4& vMat){
//    obj->customAnimation();
    QMatrix4x4 mMat(obj->getMatrix());
    _pickingProgram->setUniformValue("matrix", pMat*vMat*mMat);
    _pickingProgram->setUniformValue("referentID",QVector3D(float(obj->getSourceID())/255.0f,0,0));
//    _program->enableAttributeArray("colAttr");
//    _vao->bind();
    _vertexBuffer->bind();
    _pickingProgram->setAttributeBuffer("posAtr",GL_FLOAT,0,3);
    _pickingProgram->enableAttributeArray("posAtr");
    _indexBuffer->bind();
    glDrawElements(GL_TRIANGLES,_indexes.size(),GL_UNSIGNED_INT,0);
    _indexBuffer->release();
//    _vao->release();
    _pickingProgram->disableAttributeArray("posAtr");
    _vertexBuffer->release();
//    _program->disableAttributeArray("colAttr");
}
